package com.systar.activity.report;

import java.io.IOException;
import java.io.Writer;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class ActivityDataPool extends LinkedHashMap<String,ActivityData>
{
	/** serial */
	private static final long serialVersionUID = -2178791145478280604L;

	/** 
	 * the revision will be used to known if data has changed 
	 * and used as synchronization object
	 */
	private volatile AtomicInteger dataRevision;
	
	public ActivityDataPool()
	{
		dataRevision = new AtomicInteger(-1);
	}
	
	public int getRevision()
	{
		return dataRevision.get();
	}
	
	public void appendData(ActivityData activity)
	{
		synchronized (dataRevision)
		{
			if (this.containsKey(activity.getName()))
			{
				this.get(activity.getName()).append(activity);
			}
			else
			{
				this.put(activity.getName(), activity);
			}
			dataRevision.incrementAndGet();
		}
	}
	
	public void setData(ActivityData activity)
	{
		synchronized (dataRevision)
		{
			if (this.containsKey(activity.getName()))
			{
				this.get(activity.getName()).override(activity);
			}
			else
			{
				this.put(activity.getName(), activity);
			}
			dataRevision.incrementAndGet();
		}
	}
	
	public void writeHtmlDataFragment(Transformer dataTransformer, Writer out) throws IOException
	{
		//TODO copy data to avoid synchronization overhead
		try
		{
			final Document structDom;
			synchronized (dataRevision)
			{
				structDom =  buidXmlDocument(this);
			}
			Result result = new javax.xml.transform.stream.StreamResult(out);
			dataTransformer.transform(new DOMSource(structDom), result);
		}
		catch (TransformerException e)
		{
			throw new IOException(e);
		}
		catch (ParserConfigurationException e)
		{
			throw new IOException(e);
		}
	}
	
	public static Document buidXmlDocument(Map<String,ActivityData> data) throws IOException, ParserConfigurationException
	{
		// write XML DOM directly as so trivial, but may use third party such XStream for complex cases
		// create DOM document
		DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		Document structDom = builder.newDocument();
		
		// create Root node
		Element structRootElement = structDom.createElement("ActivityMonitor");
		structDom.appendChild(structRootElement);

		for ( ActivityData activity : data.values() )
		{
			Element activityElmt = structDom.createElement("Activity");
			structRootElement.appendChild(activityElmt);
			activityElmt.setAttribute("source", activity.getName());
			activityElmt.setAttribute("active", Boolean.toString(activity.isActive()));
			activityElmt.setAttribute("hot", Boolean.toString(activity.hasHotErrors()));
			
			
			Element messageElmt = structDom.createElement("message");
			activityElmt.appendChild(messageElmt);
			messageElmt.setTextContent(activity.getErrorMessage());
		}
		return structDom;
	}
}
